#include "ppjtools.h"

//******************************************************************************
const QString FileInfo::m_format = "%1%2%3";
//------------------------------------------------------------------------------
FileInfo::FileInfo(const QString &fullPath) : QFileInfo(fullPath)
{
}
//------------------------------------------------------------------------------
QString FileInfo::path() const
{
    return QString("%1/").arg(absolutePath());
}
//------------------------------------------------------------------------------
bool FileInfo::setPath(const QString &p)
{
    QString path = p;
    if (not path.endsWith ('/')) path.append ('/');
    setFile (m_format.arg(path).arg(name()).arg(extension()));
    return exists();
}
//------------------------------------------------------------------------------
QString FileInfo::name() const
{
    return baseName();
}
//--------------------------------------------------------------------------------------------------
bool FileInfo::setName(const QString &n)
{
    setFile (m_format.arg(path ()).arg (n).arg (extension()));
    return exists();
}
//--------------------------------------------------------------------------------------------------
QString FileInfo::extension() const
{
    return QString(".%1").arg(completeSuffix());
}
//--------------------------------------------------------------------------------------------------
bool FileInfo::setExtension(const QString &e)
{
    QString ext = e;
    if (not ext.startsWith ('.')) ext.prepend ('.');
    setFile (m_format.arg(path()).arg(name()).arg(ext));
    return exists();
}
//--------------------------------------------------------------------------------------------------
QString FileInfo::completeName() const
{
    return m_format.arg(path()).arg(name()).arg(extension());
}
//------------------------------------------------------------------------------
QString FileInfo::completeBaseName () const
{
    QString tmp = QFileInfo::completeBaseName ();
    if (isDir ()) tmp.append ('/');
    return tmp;
}


// optimisations de dlDistance
int distance[50][50];
int s1[50];
int s2[50];


// fonctions pour SOUNDEX
//----------------------------------------------------
char StringTool::soundexInternalCode (const char& car)
{
    char ret = '\0';
    switch (car)
    {
        case 'B':
        case 'P':
            ret = '1';
        break;

        case 'C':
        case 'K':
        case 'Q':
            ret = '2';
        break;

        case 'D':
        case 'T':
            ret = '3';
        break;

        case 'L':
            ret = '4';
        break;

        case 'M':
        case 'N':
            ret = '5';
        break;

        case 'R':
            ret = '6';
        break;

        case 'G':
        case 'J':
            ret = '7';
        break;

        case 'X':
        case 'Z':
        case 'S':
            ret = '8';
        break;

        case 'F':
        case 'V':
            ret = '9';
        break;
    }

    return ret;
}
//----------------------------------------------------------------------------------
QString StringTool::removeFrom (const QString &word, const QString &letters)
{
    // retourne les lettres de word qui ne sont pas dans letters
    QString ret (word);

    QString::const_iterator iter;

    for (iter = letters.constBegin () ; iter != letters.constEnd () ; iter++)
    {
        ret.remove (*iter);
    }

    return ret;
}

//---------------------------------------------------
QString StringTool::soundexCode (const QString& word)
{
    //    Supprimer les éventuels 'espace' initiaux et finaux
    //    Mettre le mot en majuscule
    QString maj = word.toLatin1 ().trimmed ().toUpper ();

    //    Garder la première lettre
    //    Conserver la première lettre de la chaîne
    QChar firstLetter = maj.at (0);
    QString tmp1 = maj.mid (1);

    //    Supprimer toutes les occurrences des lettres : a, e, h, i, o, u, w, y
    //	  (à moins que ce ne soit la première lettre du nom)

    QString tmp2 (removeFrom (tmp1, soundexLettersToDelete ()));

    //    Attribuer une valeur numérique aux lettres restantes de la manière suivante :
    //        Version pour l'anglais :
    //            1 = B, F, P, V
    //            2 = C, G, J, K, Q, S, X, Z
    //            3 = D, T
    //            4 = L
    //            5 = M, N
    //            6 = R
    //        Version pour le français :
    //            1 = B, P
    //            2 = C, K, Q
    //            3 = D, T
    //            4 = L
    //            5 = M, N
    //            6 = R
    //            7 = G, J
    //            8 = X, Z, S
    //            9 = F, V

    QString tmp3;

    foreach (const QChar& car, tmp2)
    {
        tmp3 += soundexInternalCode (car.toLatin1 ());
    }


    // Si deux lettres (ou plus) avec le même nombre sont adjacentes dans le nom d'origine,
    // ou s'il n'y a qu'un h ou un w entre elles, alors on ne retient que la première de ces lettres.

    QString tmp4 (uniqueLetters (tmp3));

    // Renvoyer les quatre premiers octets complétés par des zéros.

    QString tmp5 (QString ("%1%20000")
                  .arg (firstLetter)
                  .arg (tmp4.left (3)));

    return tmp5.left (4);

}
//--------------------------------------------------------------------------------------------------
int StringTool::dlDistance (const QString &word1, const QString &word2, const int maxDistance)
{
    // calcule la 'distance' entre 2 mots, c.a.d. le nombre d'opérations de base
    // nécessaires pour passer de l'un à l'autre
    // cf. http://fr.wikipedia.org/wiki/Distance_de_Damerau-Levenshtein
    // si cette distance est > maxDistance, on renvoie un nombre très élevé (DistanceErr=1024)

//    if (maxDistance > m_distanceMax) return DistanceErr;

    const int length1 = word1.length ();
    const int length2 = word2.length ();

    if (abs (length1 - length2) > maxDistance) return DistanceErr;

    const quint16 *data1 = (const quint16 *) word1.constData ();
    const quint16 *data2 = (const quint16 *) word2.constData ();

    // **************** calcul de la distance ****************

    int result = maxDistance + 1;

    for (int i = 1 ; i <= length1 ; ++i)
    {
        const int i_1 = i-1;
        int bestOfLine = maxDistance + 1;

        for (int j = 1 ; j <= length2 ; ++j)
        {
            const int j_1 = j-1;
            int cost = (data1[i_1] == data2[j_1]) ? 0 : 1;
            result = 1 + min (m_distance (i_1, j),						// suppression
                              m_distance (i, j_1));                     // insertion
            result = min (result,
                          m_distance (i_1, j-1) + cost);				// substitution

            // si damerau => tester les transpositions si i > 1 et j > 1
            if ((i_1 > 0) and (j_1 > 0))
            {
                if (data1[i_1] == data2[j-2] and data1[i-2] == data2[j_1])
                {
                    result = min (result,
                                  m_distance (i-2, j-2) + cost);		// transposition
                }
            }
            m_distance (i, j) = result;
            if (result < bestOfLine)
                bestOfLine = result;

        }
        if (bestOfLine > maxDistance)
            return DistanceErr;
    }

    return (result > maxDistance) ? DistanceErr : result;
}
//------------------------------------------------------------------------------
int StringTool::lDistance (const QString &word1, const QString &word2, const int maxDistance)
{
    // calcule la 'distance' entre 2 mots, c.a.d. le nombre d'opérations de base
    // nécessaires pour passer de l'un à l'autre
    // cf. http://fr.wikipedia.org/wiki/Distance_de_Damerau-Levenshtein
    // si cette distance est > maxDistance, on renvoie un nombre très élevé (DistanceErr=1024)

//    if (maxDistance > m_distanceMax) return DistanceErr;

    const int length1 = word1.length ();
    const int length2 = word2.length ();

    if (abs (length1 - length2) > maxDistance) return DistanceErr;

    const quint16 *data1 = (const quint16 *) word1.constData ();
    const quint16 *data2 = (const quint16 *) word2.constData ();

    // **************** calcul de la distance ****************

    int result = maxDistance + 1;

    for (int i = 1 ; i <= length1 ; ++i)
    {
        const int i_1 = i-1;
        int bestOfLine = maxDistance + 1;

        for (int j = 1 ; j <= length2 ; ++j)
        {
            const int j_1 = j-1;
            int cost = (data1[i_1] == data2[j_1]) ? 0 : 1;
            result = 1 + min (m_distance (i_1, j),					// suppression
                              m_distance (i, j_1));					// insertion
            result = min (result,
                          m_distance (i_1, j_1) + cost);			// substitution

            m_distance (i, j) = result;
            if (result < bestOfLine)
                bestOfLine = result;

        }
        if (bestOfLine > maxDistance) return DistanceErr;
    }

    return (result > maxDistance) ? DistanceErr : result;
}


